/*
 *  Copyright (c) M2TK Project. All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package m2tk.assistant.app.ui.component;

import cn.hutool.core.util.StrUtil;
import m2tk.assistant.app.ui.util.FormatUtil;
import m2tk.assistant.common.domain.ElementaryStream;
import m2tk.assistant.common.domain.StreamSource;
import m2tk.assistant.common.ui.RoundPanel;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import java.awt.event.MouseListener;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Objects;

public class TSSummaryPane extends RoundPanel
{
    private static final Logger log = LoggerFactory.getLogger(TSSummaryPane.class);
    private JTextField fieldSourceName;
    private JTextField fieldBitrates;
    private JTextField fieldPacketCount, fieldStreamCount, fieldProgramCount;
    private JTextField fieldTransportStreamId;
    private JTextField fieldNetworkTime;
    private JRadioButton radio188Bytes, radio204Bytes;
    private JRadioButton radioClear, radioScrambled;
    private JCheckBox checkECMPresent, checkEMMPresent;
    private JCheckBox checkPATPresent, checkPMTPresent, checkCATPresent;
    private JCheckBox checkNITActualPresent, checkNITOtherPresent, checkSDTActualPresent, checkSDTOtherPresent;
    private JCheckBox checkEITPnfActualPresent, checkEITPnfOtherPresent, checkEITSchActualPresent, checkEITSchOtherPresent;
    private JCheckBox checkBATPresent, checkTDTPresent, checkTOTPresent;

    private int maxSourceBitrate = 0;
    private int minSourceBitrate = Integer.MAX_VALUE;
    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");

    public TSSummaryPane()
    {
        initUI();
    }

    private void initUI()
    {
        fieldSourceName = readonlyTextField("");
        fieldBitrates = readonlyTextField("0 bps / 0 bps / 0 bps");
        fieldPacketCount = readonlyTextField("0");
        fieldStreamCount = readonlyTextField("0");
        fieldProgramCount = readonlyTextField("0");
        fieldTransportStreamId = readonlyTextField("未知");
        fieldNetworkTime = readonlyTextField("未知");

        JRadioButton[] packetSizeOptions = readonlySingleChoices("188 字节", "204 字节");
        radio188Bytes = packetSizeOptions[0];
        radio204Bytes = packetSizeOptions[1];

        JRadioButton[] scrambleStateOptions = readonlySingleChoices("清流", "加扰");
        radioClear = scrambleStateOptions[0];
        radioScrambled = scrambleStateOptions[1];

        JCheckBox[] caStreamOptions = readonlyOptionalChoices("ECM", "EMM");
        checkECMPresent = caStreamOptions[0];
        checkEMMPresent = caStreamOptions[1];

        JCheckBox[] psiTableOptions = readonlyOptionalChoices("PAT", "PMT", "CAT");
        checkPATPresent = psiTableOptions[0];
        checkPMTPresent = psiTableOptions[1];
        checkCATPresent = psiTableOptions[2];

        JCheckBox[] siTableOptions = readonlyOptionalChoices("NIT（当前网络）", "NIT（其它网络）", "SDT（当前流）", "SDT（其他流）",
                                                             "EIT（即时，当前流）", "EIT（即时，其它流）", "EIT（排期，当前流）", "EIT（排期，其它流）",
                                                             "BAT", "TDT", "TOT");
        checkNITActualPresent = siTableOptions[0];
        checkNITOtherPresent = siTableOptions[1];
        checkSDTActualPresent = siTableOptions[2];
        checkSDTOtherPresent = siTableOptions[3];
        checkEITPnfActualPresent = siTableOptions[4];
        checkEITPnfOtherPresent = siTableOptions[5];
        checkEITSchActualPresent = siTableOptions[6];
        checkEITSchOtherPresent = siTableOptions[7];
        checkBATPresent = siTableOptions[8];
        checkTDTPresent = siTableOptions[9];
        checkTOTPresent = siTableOptions[10];

        JPanel panel = new JPanel(new MigLayout());
        panel.setOpaque(true);
        panel.setBackground(getBackground());
        panel.add(new JLabel("输入源"), "gap 15 5, right");
        panel.add(fieldSourceName, "span 3, growx, wrap");
        panel.add(new JLabel("传输码率"), "gap 15 5, right");
        panel.add(fieldBitrates, "span 3, growx");
        panel.add(new JLabel("（最大 / 最小 / 实时）"), "span 2, wrap");
        panel.add(new JLabel("传输包数"), "gap 15 5, right");
        panel.add(fieldPacketCount, "width 120, growx");
        panel.add(new JLabel("基本流数"), "gap 15 5, right");
        panel.add(fieldStreamCount, "width 120, growx, wrap");
        panel.add(new JLabel("传输流号"), "gap 15 5, right");
        panel.add(fieldTransportStreamId, "width 120, growx");
        panel.add(new JLabel("节目数"), "gap 15 5, right");
        panel.add(fieldProgramCount, "width 120, growx, wrap");
        panel.add(new JLabel("包大小"), "gap 15 5, right");
        panel.add(radio188Bytes);
        panel.add(radio204Bytes, "wrap");
        panel.add(new JLabel("加扰状态"), "gap 15 5, right");
        panel.add(radioClear);
        panel.add(radioScrambled, "wrap");
        panel.add(new JLabel("条件接收"), "gap 15 5, right");
        panel.add(checkECMPresent);
        panel.add(checkEMMPresent, "wrap");
        panel.add(new JLabel("节目信息"), "gap 15 5, right");
        panel.add(checkPATPresent);
        panel.add(checkPMTPresent);
        panel.add(checkCATPresent, "wrap");
        panel.add(new JLabel("业务信息"), "gap 15 5, right");
        panel.add(checkNITActualPresent);
        panel.add(checkNITOtherPresent);
        panel.add(checkSDTActualPresent);
        panel.add(checkSDTOtherPresent, "wrap");
        panel.add(checkEITPnfActualPresent, "skip 1");
        panel.add(checkEITPnfOtherPresent);
        panel.add(checkEITSchActualPresent);
        panel.add(checkEITSchOtherPresent, "wrap");
        panel.add(checkBATPresent, "skip 1");
        panel.add(checkTDTPresent);
        panel.add(checkTOTPresent, "wrap");
        panel.add(new JLabel("网络时间"), "gap 15 5, right");
        panel.add(fieldNetworkTime, "grow");

        setLayout(new MigLayout("insets 10", "[][grow][]", "[25!][pref!][][grow]"));
        add(new JLabel("传输流概览",
                       StreamInfoNaviPane.BOARD_ICON,
                       SwingConstants.TRAILING), "left, wrap");
        add(separator(), "span 3, grow, wrap");
        add(panel, "span 3 2, grow");
    }

    public void updateSummary(StreamSource source, List<ElementaryStream> streams, OffsetDateTime time)
    {
        String oldSourceName = fieldSourceName.getText();
        if (!Objects.equals(oldSourceName, source.getName()))
        {
            fieldSourceName.setText(source.getName());
        }

        if (source.getBitrate() > 0)
        {
            maxSourceBitrate = Math.max(maxSourceBitrate, source.getBitrate());
            minSourceBitrate = Math.min(minSourceBitrate, source.getBitrate());

            fieldBitrates.setText(String.format("%s / %s / %s",
                                                FormatUtil.formatBitrate(maxSourceBitrate),
                                                FormatUtil.formatBitrate(minSourceBitrate),
                                                FormatUtil.formatBitrate(source.getBitrate())));
        } else
        {
            fieldBitrates.setText(String.format("%s / %s / %s",
                                                FormatUtil.formatBitrate(0),
                                                FormatUtil.formatBitrate(0),
                                                FormatUtil.formatBitrate(0)));
        }

        fieldPacketCount.setText(String.format("%,d", source.getPacketCount()));
        fieldStreamCount.setText(String.format("%,d", source.getStreamCount()));
        fieldProgramCount.setText(String.format("%,d", source.getProgramCount()));
        fieldTransportStreamId.setText("" + source.getTransportStreamId());

        if (source.getFrameSize() == 188)
            radio188Bytes.setSelected(true);
        else
            radio204Bytes.setSelected(true);
        if (source.isScrambled())
            radioScrambled.setSelected(true);
        else
            radioClear.setSelected(true);
        checkECMPresent.setSelected(source.isEcmPresent());
        checkEMMPresent.setSelected(source.isEmmPresent());
        checkPATPresent.setSelected(source.isPatPresent());
        checkPMTPresent.setSelected(source.isPmtPresent());
        checkCATPresent.setSelected(source.isCatPresent());
        checkNITActualPresent.setSelected(source.isNitActualPresent());
        checkNITOtherPresent.setSelected(source.isNitOtherPresent());
        checkSDTActualPresent.setSelected(source.isSdtActualPresent());
        checkSDTOtherPresent.setSelected(source.isSdtOtherPresent());
        checkEITPnfActualPresent.setSelected(source.isEitPnfActualPresent());
        checkEITPnfOtherPresent.setSelected(source.isEitPnfOtherPresent());
        checkEITSchActualPresent.setSelected(source.isEitSchActualPresent());
        checkEITSchOtherPresent.setSelected(source.isEitSchOtherPresent());
        checkBATPresent.setSelected(source.isBatPresent());
        checkTDTPresent.setSelected(source.isTdtPresent());
        checkTOTPresent.setSelected(source.isTotPresent());

        for (ElementaryStream stream : streams)
        {
            if (StrUtil.contains(stream.getDescription(), "ECM"))
                checkECMPresent.setSelected(true);
            else if (StrUtil.contains(stream.getDescription(), "EMM"))
                checkEMMPresent.setSelected(true);
        }

        if (time == null)
        {
            fieldNetworkTime.setText("未知");
        } else
        {
            LocalDateTime utcTime = time.atZoneSameInstant(ZoneId.systemDefault())
                                        .toLocalDateTime();
            fieldNetworkTime.setText(utcTime.format(formatter));
        }
    }

    private JTextField readonlyTextField(String text)
    {
        JTextField field = new JTextField(text);
        field.setEditable(false);
        field.setFocusable(false);
        return field;
    }

    private JRadioButton[] readonlySingleChoices(String... options)
    {
        JRadioButton[] buttons = new JRadioButton[options.length];
        ButtonGroup group = new ButtonGroup();

        for (int i = 0; i < options.length; i++)
        {
            JRadioButton button = new JRadioButton(options[i]);

            // 只能通过API修改单选按钮的状态，不响应UI操作事件。
            button.setFocusable(false);
            MouseListener[] listeners = button.getMouseListeners();
            for (MouseListener listener : listeners)
                button.removeMouseListener(listener);

            buttons[i] = button;
            group.add(button);
        }

        return buttons;
    }

    private JCheckBox[] readonlyOptionalChoices(String... options)
    {
        JCheckBox[] buttons = new JCheckBox[options.length];

        for (int i = 0; i < options.length; i++)
        {
            JCheckBox button = new JCheckBox(options[i]);

            // 只能通过API修改复选按钮的状态，不响应UI操作事件。
            button.setFocusable(false);
            MouseListener[] listeners = button.getMouseListeners();
            for (MouseListener listener : listeners)
                button.removeMouseListener(listener);

            buttons[i] = button;
        }

        return buttons;
    }
}
